package core;

import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.Random;

import javax.swing.JComboBox;

import core.data.Constants;
import core.data.MeasuredValues;
import core.router.Router;
import core.schedulers.DataKind;
import core.schedulers.PacketScheduler;
import eduni.simanim.Anim_applet;
import eduni.simjava.Sim_system;
import eduni.simjava.distributions.Sim_random_obj;

public class CommandLine extends Anim_applet {

	Panel inputs;
	Label meanPacketSizeLan, meanPacketSizeNet, bandwidth,
			lanMeanPacketInterval, netMeanPacketInterval, routerQueueCapacity,
			daysOfWeek, tamanhoMedioDaFilaLan, tamanhoMedioDaFilaNet,
			tamanhoMedioDaFilaLanValorEsperado,
			tamanhoMedioDaFilaNetValorEsperado, utilizacaoMediaLan,
			utilizacaoMediaNet, utilizacaoMediaLanValorEsperado,
			utilizacaoMediaNetValorEsperado;
	TextField meanPacketSizeLanField, meanPacketSizeNetField, bandwidthField,
			lanMeanPacketIntervalField, netMeanPacketIntervalField,
			routerQueueCapacityField, tamanhoMedioDaFilaLanValue,
			tamanhoMedioDaFilaNetValue,
			tamanhoMedioDaFilaLanValueValorEsperado,
			tamanhoMedioDaFilaNetValueValorEsperado, utilizacaoMediaLanValue,
			utilizacaoMediaNetValue, utilizacaoMediaLanValueValorEsperado,
			utilizacaoMediaNetValueValorEsperado;
	JComboBox daysOfWeekComboBox;
	String diaDaSemana = Constants.MONDAY;

	// initialise the gui inputs
	public void anim_init() {

		inputs = new Panel();
		inputs.setLayout(new GridLayout(0, 2));

		resize(1024, 840);

		meanPacketSizeLan = new Label(
				"Tamanho dos pacotes da LAN (em Bytes): ", Label.RIGHT);
		inputs.add(meanPacketSizeLan);
		meanPacketSizeLanField = new TextField(String
				.valueOf(MeasuredValues.MEAN_PACKET_SIZE_LAN), 5);
		inputs.add(meanPacketSizeLanField);

		meanPacketSizeNet = new Label(
				"Tamanho dos pacotes da Internet (em Bytes): ", Label.RIGHT);
		inputs.add(meanPacketSizeNet);
		meanPacketSizeNetField = new TextField(String
				.valueOf(MeasuredValues.MEAN_PACKET_SIZE_NET), 5);
		inputs.add(meanPacketSizeNetField);

		bandwidth = new Label("Largura de banda (em Mbps): ", Label.RIGHT);
		inputs.add(bandwidth);
		bandwidthField = new TextField(String
				.valueOf(MeasuredValues.BART_BANDWIDTH), 5);
		inputs.add(bandwidthField);

		daysOfWeek = new Label("Dia da semana: ", Label.RIGHT);
		inputs.add(daysOfWeek);
		String days[] = { Constants.MONDAY, Constants.TUESDAY,
				Constants.WEDNESDAY, Constants.THURSDAY, Constants.FRIDAY,
				Constants.SATURDAY, Constants.SUNDAY };
		daysOfWeekComboBox = new JComboBox(days);
		daysOfWeekComboBox.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent ie) {
				diaDaSemana = (String) daysOfWeekComboBox.getSelectedItem();
				;
			}
		});
		inputs.add(daysOfWeekComboBox);

		// Valor esperado: tamanho medio da fila NET
		tamanhoMedioDaFilaLanValorEsperado = new Label(
				"Valor esperado do tamanho medio da fila LAN: ", Label.RIGHT);
		inputs.add(tamanhoMedioDaFilaLanValorEsperado);
		tamanhoMedioDaFilaLanValueValorEsperado = new TextField("0.000000",
				Label.LEFT);
		inputs.add(tamanhoMedioDaFilaLanValueValorEsperado);
		tamanhoMedioDaFilaLanValueValorEsperado.setEditable(false);

		// Valor obtido: tamanho medio da fila LAN
		tamanhoMedioDaFilaLan = new Label("Tamanho medio da fila LAN: ",
				Label.RIGHT);
		inputs.add(tamanhoMedioDaFilaLan);
		tamanhoMedioDaFilaLanValue = new TextField("0.000000", Label.LEFT);
		inputs.add(tamanhoMedioDaFilaLanValue);
		tamanhoMedioDaFilaLanValue.setEditable(false);

		// Valor esperado: tamanho medio da fila NET
		tamanhoMedioDaFilaNetValorEsperado = new Label(
				"Valor esperado do tamanho medio da fila NET: ", Label.RIGHT);
		inputs.add(tamanhoMedioDaFilaNetValorEsperado);
		tamanhoMedioDaFilaNetValueValorEsperado = new TextField("0.000000",
				Label.LEFT);
		inputs.add(tamanhoMedioDaFilaNetValueValorEsperado);
		tamanhoMedioDaFilaNetValueValorEsperado.setEditable(false);

		// Valor obtido: tamanho medio da fila NET
		tamanhoMedioDaFilaNet = new Label("Tamanho medio da fila NET: ",
				Label.RIGHT);
		inputs.add(tamanhoMedioDaFilaNet);
		tamanhoMedioDaFilaNetValue = new TextField("0.000000", Label.LEFT);
		inputs.add(tamanhoMedioDaFilaNetValue);
		tamanhoMedioDaFilaNetValue.setEditable(false);

		// Valor esperado: utilizacao media LAN
		utilizacaoMediaLanValorEsperado = new Label(
				"Valor esperado da utilizacao media LAN: ", Label.RIGHT);
		inputs.add(utilizacaoMediaLanValorEsperado);
		utilizacaoMediaLanValueValorEsperado = new TextField("0.000000",
				Label.LEFT);
		inputs.add(utilizacaoMediaLanValueValorEsperado);
		utilizacaoMediaLanValueValorEsperado.setEditable(false);

		// Valor obtido: utilizacao media LAN
		utilizacaoMediaLan = new Label("Utilizacao media LAN: ", Label.RIGHT);
		inputs.add(utilizacaoMediaLan);
		utilizacaoMediaLanValue = new TextField("0.000000", Label.LEFT);
		inputs.add(utilizacaoMediaLanValue);
		utilizacaoMediaLanValue.setEditable(false);

		// Valor esperado: utilizacao media NET
		utilizacaoMediaNetValorEsperado = new Label(
				"Valor esperado da utilizacao media NET: ", Label.RIGHT);
		inputs.add(utilizacaoMediaNetValorEsperado);
		utilizacaoMediaNetValueValorEsperado = new TextField("0.000000",
				Label.LEFT);
		inputs.add(utilizacaoMediaNetValueValorEsperado);
		utilizacaoMediaNetValueValorEsperado.setEditable(false);

		// Valor obtido: utilizacao media NET
		utilizacaoMediaNet = new Label("Utilizacao media NET: ", Label.RIGHT);
		inputs.add(utilizacaoMediaNet);
		utilizacaoMediaNetValue = new TextField("0.000000", Label.LEFT);
		inputs.add(utilizacaoMediaNetValue);
		utilizacaoMediaNetValue.setEditable(false);

		this.add("North", inputs);
	}

	// Setup the animation
	public void anim_layout() {

		double meanPacketSizeLan, meanPacketSizeNet, bandwidth, lanMeanPacketInterval, netMeanPacketInterval, vazaoNet, vazaoLan;

		meanPacketSizeLan = Double.valueOf(meanPacketSizeLanField.getText())
				.doubleValue();
		meanPacketSizeNet = Double.valueOf(meanPacketSizeNetField.getText())
				.doubleValue();
		bandwidth = Double.valueOf(bandwidthField.getText()).doubleValue() * 1000000;

		if (diaDaSemana.equals(Constants.MONDAY)) {

			lanMeanPacketInterval = MeasuredValues.MONDAY_MEAN_INTERVAL_LAN;
			netMeanPacketInterval = MeasuredValues.MONDAY_MEAN_INTERVAL_NET;
			vazaoNet = MeasuredValues.MONDAY_THROUGHPUT_NET;
			vazaoLan = MeasuredValues.MONDAY_THROUGHPUT_LAN;

		} else if (diaDaSemana.equals(Constants.TUESDAY)) {

			lanMeanPacketInterval = MeasuredValues.TUESDAY_MEAN_INTERVAL_LAN;
			netMeanPacketInterval = MeasuredValues.TUESDAY_MEAN_INTERVAL_NET;
			vazaoNet = MeasuredValues.TUESDAY_THROUGHPUT_NET;
			vazaoLan = MeasuredValues.TUESDAY_THROUGHPUT_LAN;

		} else if (diaDaSemana.equals(Constants.WEDNESDAY)) {

			lanMeanPacketInterval = MeasuredValues.WEDNESDAY_MEAN_INTERVAL_LAN;
			netMeanPacketInterval = MeasuredValues.WEDNESDAY_MEAN_INTERVAL_NET;
			vazaoNet = MeasuredValues.WEDNESDAY_THROUGHPUT_NET;
			vazaoLan = MeasuredValues.WEDNESDAY_THROUGHPUT_LAN;

		} else if (diaDaSemana.equals(Constants.THURSDAY)) {

			lanMeanPacketInterval = MeasuredValues.THURSDAY_MEAN_INTERVAL_LAN;
			netMeanPacketInterval = MeasuredValues.THURSDAY_MEAN_INTERVAL_NET;
			vazaoNet = MeasuredValues.THURSDAY_THROUGHPUT_NET;
			vazaoLan = MeasuredValues.THURSDAY_THROUGHPUT_LAN;

		} else if (diaDaSemana.equals(Constants.FRIDAY)) {

			lanMeanPacketInterval = MeasuredValues.FRIDAY_MEAN_INTERVAL_LAN;
			netMeanPacketInterval = MeasuredValues.FRIDAY_MEAN_INTERVAL_NET;
			vazaoNet = MeasuredValues.FRIDAY_THROUGHPUT_NET;
			vazaoLan = MeasuredValues.FRIDAY_THROUGHPUT_LAN;

		} else if (diaDaSemana.equals(Constants.SATURDAY)) {

			lanMeanPacketInterval = MeasuredValues.SATURDAY_MEAN_INTERVAL_LAN;
			netMeanPacketInterval = MeasuredValues.SATURDAY_MEAN_INTERVAL_NET;
			vazaoNet = MeasuredValues.SATURDAY_THROUGHPUT_NET;
			vazaoLan = MeasuredValues.SATURDAY_THROUGHPUT_LAN;

		} else {

			lanMeanPacketInterval = MeasuredValues.SUNDAY_MEAN_INTERVAL_LAN;
			netMeanPacketInterval = MeasuredValues.SUNDAY_MEAN_INTERVAL_NET;
			vazaoNet = MeasuredValues.SUNDAY_THROUGHPUT_NET;
			vazaoLan = MeasuredValues.SUNDAY_THROUGHPUT_LAN;

		}

		double xLan = (((meanPacketSizeLan * 8) / bandwidth) - lanMeanPacketInterval)
				/ ((meanPacketSizeLan * 8) / bandwidth);
		double xNet = (((meanPacketSizeNet * 8) / bandwidth) - netMeanPacketInterval)
				/ ((meanPacketSizeNet * 8) / bandwidth);

		double tamanhoMedioFilaLan = 4950 * xLan;
		double tamanhoMedioFilaNet = 4950 * xNet;

		tamanhoMedioFilaLan /= 100;
		tamanhoMedioFilaNet /= 100;

		double saidaLan = bandwidth / (meanPacketSizeLan * 8); // quantos
		// pacotes
		// atende por
		// segundo na
		// lan
		double saidaNet = bandwidth / (meanPacketSizeNet * 8); // quantos
		// pacotes
		// atende por
		// segundo na
		// net

		if (vazaoNet > saidaNet)
			vazaoNet = saidaNet;
		else
			tamanhoMedioFilaNet = 0;

		if (vazaoLan > saidaLan)
			vazaoLan = saidaLan;
		else
			tamanhoMedioFilaLan = 0;

		setTamanhoMedioEsperadoDaFilaLanValue(tamanhoMedioFilaLan);
		setTamanhoMedioEsperadoDaFilaNetValue(tamanhoMedioFilaNet);

		double utilizacaoLan = ((meanPacketSizeLan * 8) / bandwidth) * vazaoLan;
		double utilizacaoNet = ((meanPacketSizeNet * 8) / bandwidth) * vazaoNet;

		utilizacaoMediaLanValueValorEsperado.setText(format(utilizacaoLan));
		utilizacaoMediaNetValueValorEsperado.setText(format(utilizacaoNet));

		Random m = new Random();
		Sim_random_obj generator = new Sim_random_obj("Seed generator", m
				.nextInt(Integer.MAX_VALUE));
		Sim_random_obj generator2 = new Sim_random_obj("Seed generator2", m
				.nextInt(Integer.MAX_VALUE));

		long seed1 = generator.get_seed();
		PacketScheduler lan = new PacketScheduler("LAN", 10, 60, "lan",
				lanMeanPacketInterval, meanPacketSizeLan, seed1,
				meanPacketSizeLan);
		long seed2 = generator2.get_seed();
		PacketScheduler net = new PacketScheduler("NET", 370, 60, "net",
				netMeanPacketInterval, meanPacketSizeNet, seed2,
				meanPacketSizeNet);

		Router roteadorLan = new Router("RouterLan", 200, 60, "router",
				bandwidth, DataKind.LAN, this, meanPacketSizeLan * 8);
		Router roteadorNet = new Router("RouterNet", 200, 60, "router",
				bandwidth, DataKind.INTERNET, this, meanPacketSizeNet * 8);

		lan.router = roteadorLan;
		net.router = roteadorNet;
		
		Sim_system.link_ports("LAN", "Out", "RouterLan", "In1");
		Sim_system.link_ports("NET", "Out", "RouterNet", "In2");
		Sim_system.link_ports("RouterNet", "Out2", "LAN", "In");
		Sim_system.link_ports("RouterLan", "Out1", "NET", "In");
	}

	public void sim_setup() {
		Sim_system.set_transient_condition(Sim_system.TIME_ELAPSED, 10000);
		Sim_system.set_termination_condition(Sim_system.TIME_ELAPSED, 1, false);
		Sim_system.set_output_analysis(Sim_system.IND_REPLICATIONS, 2, 0.95);
		Sim_system.set_report_detail(false, false);
		Sim_system.generate_graphs(false);
		Sim_system.set_trace_level(1);
		Sim_system.set_trace_detail(true, true, false);
	}

	// Choose animation output detail
	public void anim_output() {
		generate_report(false);
		generate_messages(false);
	}

	public String format(double valor) {
		DecimalFormat formato = new DecimalFormat("0.000000");
		formato.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
		return formato.format(valor);
	}

	public double getValorMedio(double valor) {
		if (valor < 0)
			return 0;
		return valor;
	}

	public void setTamanhoMedioEsperadoDaFilaLanValue(
			double tamanhoMedioDaFilaLanValue) {
		if (tamanhoMedioDaFilaLanValue > 1) {
			this.tamanhoMedioDaFilaLanValueValorEsperado
					.setText(format(tamanhoMedioDaFilaLanValue));
		}
	}

	public void setTamanhoMedioDaFilaLanValue(double tamanhoMedioDaFilaLanValue) {
		if (tamanhoMedioDaFilaLanValue > 1) {
			this.tamanhoMedioDaFilaLanValue
					.setText(format(tamanhoMedioDaFilaLanValue));
		}
	}

	public void setTamanhoMedioEsperadoDaFilaNetValue(
			double tamanhoMedioDaFilaNetValue) {
		if (tamanhoMedioDaFilaNetValue > 1) {
			this.tamanhoMedioDaFilaNetValueValorEsperado
					.setText(format(tamanhoMedioDaFilaNetValue));
		}
	}

	public void setTamanhoMedioDaFilaNetValue(double tamanhoMedioDaFilaNetValue) {
		if (tamanhoMedioDaFilaNetValue > 1) {
			this.tamanhoMedioDaFilaNetValue
					.setText(format(tamanhoMedioDaFilaNetValue));
		}
	}

	public void setUtilizacaoMediaLanValue(double utilizacaoMediaLanValue) {
		this.utilizacaoMediaLanValue
				.setText(format(getValorMedio(utilizacaoMediaLanValue)));
	}

	public void setUtilizacaoMediaNetValue(double utilizacaoMediaNetValue) {
		this.utilizacaoMediaNetValue
				.setText(format(getValorMedio(utilizacaoMediaNetValue)));
	}

}